package com.ken.concurrencystudy.example;

import com.ken.concurrencystudy.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 双重校验实现单例模式 volatile字段禁止编译器对instance的赋值语句进行指令重排
 * @author liushiyu@changingedu.com
 * @date 5/6/2020 13:59
 */
@Slf4j
@NotThreadSafe
public final class DoubleCheckSingletonWithVolatile {

    private static final int CORE_POOL_SIZE = 2;
    private static final int MAX_POOL_SIZE = 100;
    private static final int QUEUE_CAPACITY = 100;
    private static final Long KEEP_ALIVE_TIME = 1L;
    private static volatile DoubleCheckSingletonWithVolatile instance = null;

    private DoubleCheckSingletonWithVolatile(){

    }

    public void sayHello(){
        log.info("hello");
    }

    public final static DoubleCheckSingletonWithVolatile getInstance(){
        if (instance == null){
            synchronized (DoubleCheckSingletonWithVolatile.class){
                if (instance == null){
                    /*
                    编译器可能对该条语句进行指令重排
                    1：分配对象的内存空间
                    memory = allocate();
                    2：初始化对象
                    ctorInstance(memory);
                    3：设置instance指向刚分配的内存地址
                    instance = memory;
                    -------------------------------
                    1：分配对象的内存空间
                    memory = allocate();
                    3：设置instance指向刚分配的内存地址
                    instance = memory;
                    2：初始化对象
                    ctorInstance(memory);
                     */
                    instance = new DoubleCheckSingletonWithVolatile();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE
                ,MAX_POOL_SIZE
                ,KEEP_ALIVE_TIME, TimeUnit.SECONDS
                ,new ArrayBlockingQueue<Runnable>(QUEUE_CAPACITY));
        for (int i = 0; i < CORE_POOL_SIZE; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                     DoubleCheckSingletonWithVolatile.getInstance().sayHello();
                }
            });
        }
        threadPoolExecutor.shutdown();
        while (!threadPoolExecutor.isShutdown()){

        }
        System.out.println("hello world!");
    }
}
